#region OpenNETCF Copyright Information
/*
Copyright (c) 2007 OpenNETCF Consulting, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial 
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#endregion

using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using OpenNETCF.Win32;
using OpenNETCF.Windows.Forms;
using Microsoft.WindowsCE.Forms;

namespace OpenNETCF.WindowsMobile.Ink
{
    /// <summary>
    /// Windows Mobile 6 Professional InkCanvas Control.  See http://msdn2.microsoft.com/en-us/library/bb431715.aspx for more details
    /// </summary>
    public class InkCanvas: Control2
    {
        #region Static initialization
        private static bool m_bInitialized = false;
        static InkCanvas()
        {
            try
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsRunTime)
                {
                    if (!m_bInitialized)
                        m_bInitialized = NativeMethods.InitInkControls();
                }
            }
            catch (MissingMethodException)
            {
            }
        }
        //
        // InkCanvas Window Class constant
        //
        private static readonly string WC_INKCANVAS = "WLINKCANVAS";
        #endregion

        #region Overrides
        protected override CreateParams CreateParams
        {
            get
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return new CreateParams() ;

                if (!m_bInitialized)
                    throw new PlatformNotSupportedException("Ink control is not supported on this platform");
                CreateParams p = base.CreateParams;
                p.ClassName = WC_INKCANVAS;
                p.Style = (int)((int)WS.VISIBLE | (int)WS.CHILD | (int)WS.HSCROLL | (int)WS.VSCROLL);
                return p;
            }
        }

        protected override void OnParentChanged(EventArgs e)
        {
            base.OnParentChanged(e);

            //Set the mode to Ink
            Mode = IC_EDITINGMODE.INK;
        }

        protected override void OnNotifyMessage(ref Microsoft.WindowsCE.Forms.Message m)
        {
            NMHDR nh = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(OpenNETCF.Win32.NMHDR));
            NMSTROKE ns;
            switch ((ICMN)nh.code)
            {
                case ICMN.CHAR:
                    NMCHR nch = (NMCHR)Marshal.PtrToStructure(m.LParam, typeof(NMCHR));
                    OnChar(nch);
                    System.Diagnostics.Debug.WriteLine("Char: " + (char)nch.ch);
                    break;
                case ICMN.STROKEADDED:
                    ns = (NMSTROKE)Marshal.PtrToStructure(m.LParam, typeof(NMSTROKE));
                    OnStrokeAdded(ns);
                    System.Diagnostics.Debug.WriteLine("Stroke added: " + ns.lStrokeID.ToString());
                    break;
                case ICMN.STROKEDELETED:
                    ns = (NMSTROKE)Marshal.PtrToStructure(m.LParam, typeof(NMSTROKE));
                    OnStrokeDeleted(ns);
                    System.Diagnostics.Debug.WriteLine("Stroke deleted: " + ns.lStrokeID.ToString());
                    break;
            }
            
            base.OnNotifyMessage(ref m);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                return;
            CanvasSize = ClientSize;
        }

        #endregion

        #region Event handlers
        protected virtual void OnChar(NMCHR hdr)
        {
            if (CharacterAdded != null)
                CharacterAdded(this, new KeyEventArgs((Keys)hdr.ch));
        }

        protected virtual void OnStrokeAdded(NMSTROKE hdr)
        {
            if (StrokeAdded != null)
                StrokeAdded(this, new StrokeEventArgs(hdr.lStrokeID));
        }

        protected virtual void OnStrokeDeleted(NMSTROKE hdr)
        {
            if (StrokeDeleted != null)
                StrokeDeleted(this, new StrokeEventArgs(hdr.lStrokeID));
        }
        #endregion

        #region Events
        public event EventHandler<StrokeEventArgs> StrokeAdded;
        public event EventHandler<StrokeEventArgs> StrokeDeleted;
        public event KeyEventHandler CharacterAdded;
        #endregion

        #region Public properties
        public IC_EDITINGMODE Mode
        {
            get
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return IC_EDITINGMODE.INK;
                return (IC_EDITINGMODE)SendMessage(ICM.GETMODE, 0, 0);
            }
            set
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return;
                SendMessage(ICM.SETMODE, (int)value, 0);
            }
        }

        public Interfaces.IInkDisp Ink
        {
            get
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return null;
                IntPtr pInk;
                NativeMethods.SendMessage(childHandle, (int)ICM.GETINK, 0, out pInk);
                if (pInk == IntPtr.Zero)
                    return null;
                return (Interfaces.IInkDisp)Marshal.GetObjectForIUnknown(pInk);
            }
        }


        public override Color BackColor
        {
            get
            {
                return base.BackColor;
            }
            set
            {
                base.BackColor = value;
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return;
                SendMessage(ICM.SETBKGNDCOLOR, 0, (value.B << 16) | (value.G << 8) | value.R);
            }
        }

        public int ZoomLevel
        {
            get
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return 1;
                return SendMessage(ICM.GETZOOM, 0, 0);
            }
            set
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return;
                SendMessage(ICM.SETZOOM, value, 0);
            }
        }

        public Size CanvasSize
        {
            get
            {
                Size sz = Size.Empty;
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return sz;
                NativeMethods.SendMessage(childHandle, (int)ICM.GETCANVASSIZE, 0, ref sz);
                return sz;
            }

            set
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return;
                NativeMethods.SendMessage(childHandle, (int)ICM.SETCANVASSIZE, 0, ref value);
            }
        }

        public ICMASK NotificationMask
        {
            get
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return ICMASK.DEFAULT;
                return (ICMASK)SendMessage(ICM.GETNOTIFICATIONMASK, 0, 0);
            }
            set
            {
                if (OpenNETCF.Windows.Forms.StaticMethods.IsDesignTime)
                    return;
                SendMessage(ICM.SETNOTIFICATIONMASK, (int)value, 0);
            }
        }

        public string RecognizedText
        {
            get
            {
                return Ink.Strokes.RecognitionResult.TopString;
            }
        }

        public PENSTYLE PenStyle
        {
            get
            {
                if (StaticMethods.IsDesignTime)
                    return new PENSTYLE();
                return GetPenStyle();
            }
            set
            {
                if(StaticMethods.IsRunTime)
                    SetPenStyle(value);
            }
        }

        #endregion

        #region Public methods
        public bool CanCopy()
        {
            return SendMessage(ICM.CANCOPY, 0, 0) != 0;
        }

        public bool CanPaste()
        {
            return SendMessage(ICM.CANPASTE, 0, 0) != 0;
        }

        public bool CanRedo()
        {
            return SendMessage(ICM.CANREDO, 0, 0) != 0;
        }

        public bool CanUndo()
        {
            return SendMessage(ICM.CANUNDO, 0, 0) != 0;
        }

        public void ClearCanvas()
        {
            SendMessage(ICM.CLEARCANVAS, 0, 0);
        }

        public void EraseAllInk()
        {
            SendMessage(ICM.ERASEALLINK, 0, 0);
        }

        public object GetInkData(IC_INKENCODING encoding)
        {
            Interfaces.VARIANT vv = new OpenNETCF.WindowsMobile.Ink.Interfaces.VARIANT();
            NativeMethods.SendMessage(childHandle, (int)ICM.GETINKDATA, (int)encoding, ref vv);
            try
            {
                if ((VarEnum)vv.Vt == (VarEnum.VT_ARRAY | VarEnum.VT_UI1))
                {
                    IntPtr pData;
                    int lb, ub;
                    NativeMethods.SafeArrayGetLBound((IntPtr)vv.Val1, 1, out lb);
                    NativeMethods.SafeArrayGetUBound((IntPtr)vv.Val1, 1, out ub);
                    NativeMethods.SafeArrayAccessData((IntPtr)vv.Val1, out pData);
                    int cb = ub - lb;
                    byte[] data = new byte[cb];
                    Marshal.Copy(pData, data, 0, cb);
                    NativeMethods.SafeArrayUnaccessData((IntPtr)vv.Val1);
                    return data;
                }
                else if ((VarEnum)vv.Vt == VarEnum.VT_BSTR)
                    return Marshal.PtrToStringBSTR((IntPtr)vv.Val1);
                else
                    return null;
            }
            finally
            {
                NativeMethods.VariantClear(ref vv);
            }
        }

        public Bitmap GetInkDataAsBitmap()
        {
            IntPtr hBitmap;
            NativeMethods.SendMessage(childHandle, (int)ICM.GETINKDATAASBITMAP, 0, out hBitmap);
            Bitmap bm = Bitmap.FromHbitmap(hBitmap);
            // TODO (delete hBitmap)
            return bm;
        }

        public void LoadInkData(string base64Data)
        {
            Interfaces.VARIANT v = new OpenNETCF.WindowsMobile.Ink.Interfaces.VARIANT();
            v.Vt = VarEnum.VT_BSTR;
            v.Val1 = (int)Marshal.StringToBSTR(base64Data);
            NativeMethods.SendMessage(childHandle, (int)ICM.LOADINKDATA, 0, ref v);
            NativeMethods.VariantClear(ref v);
            CanvasSize = ClientSize;
        }

        public void LoadInkData(byte[] nativeDataOrGif)
        {
            Interfaces.VARIANT v = new OpenNETCF.WindowsMobile.Ink.Interfaces.VARIANT();
            v.Vt = VarEnum.VT_ARRAY | VarEnum.VT_UI1;
            IntPtr pArr = NativeMethods.SafeArrayCreateVector(VarEnum.VT_UI1, 0, nativeDataOrGif.Length);
            IntPtr pData;
            NativeMethods.SafeArrayAccessData(pArr, out pData);
            Marshal.Copy(nativeDataOrGif, 0, pData, nativeDataOrGif.Length);
            NativeMethods.SafeArrayUnaccessData(pArr);
            v.Val1 = pArr.ToInt32();
            NativeMethods.SendMessage(childHandle, (int)ICM.LOADINKDATA, 0, ref v);
            NativeMethods.VariantClear(ref v);
            CanvasSize = ClientSize;
        }

        public void SetPenStyle(PENSTYLE style)
        {
            NativeMethods.SendMessage(childHandle, (int)ICM.SETPENSTYLE, 0, ref style);
        }

        public void SetPenStyle(float width, Color clr, IC_PENTYPE type)
        {
            PENSTYLE style = new PENSTYLE();
            style.cbSize = Marshal.SizeOf(typeof(PENSTYLE));
            style.crColor = (clr.B << 16) | (clr.G << 8) | clr.R;
            style.dPenWidth = width;
            NativeMethods.SendMessage(childHandle, (int)ICM.SETPENSTYLE, 0, ref style);
        }

        public PENSTYLE GetPenStyle()
        {
            PENSTYLE style = new PENSTYLE();
            style.cbSize = Marshal.SizeOf(typeof(PENSTYLE));
            NativeMethods.SendMessage(childHandle, (int)ICM.GETPENSTYLE, 0, ref style);
            return style;
        }

        public void Redo()
        {
            SendMessage(ICM.REDO, 0, 0);
        }

        public void Undo()
        {
            SendMessage(ICM.UNDO, 0, 0);
        }

        public void SetAutoScroll(bool fEnabled)
        {
            SendMessage(ICM.SETAUTOSCROLL, fEnabled? -1: 0, 0);
        }

        public void SetAutoExpand(bool fEnabled)
        {
            SendMessage(ICM.SETAUTOEXPAND, fEnabled ? -1 : 0, 0);
        }

        public int GetUndoCount()
        {
            return SendMessage(ICM.GETUNDOCOUNT, 0, 0);
        }

        public void SetCanvasColor(Color clr)
        {
            SendMessage(ICM.SETCANVASCOLOR, 0, (clr.B << 16) | (clr.G << 8) | clr.R);
        }

        public void Paste()
        {
            SendMessage(WM.PASTE, 0, 0);
        }

        public void Copy()
        {
            SendMessage(WM.COPY, 0, 0);
        }

        public void Cut()
        {
            SendMessage(WM.CUT, 0, 0);
        }
        #endregion

        #region Helper methods
        private int SendMessage(int m, int wParam, int lParam)
        {
            Message msg =
                Message.Create(childHandle, (int)m, (IntPtr)wParam, (IntPtr)lParam);
            MessageWindow.SendMessage(ref msg);
            return (int)msg.Result;
        }

        private int SendMessage(ICM m, int wParam, int lParam)
        {
            return SendMessage((int)m, wParam, lParam);
        }

        private int SendMessage(WM m, int wParam, int lParam)
        {
            return SendMessage((int)m, wParam, lParam);
        }
        #endregion


     
    }

    /// <summary>
    /// Event arguments for ICMN_StrokeAdded/Deleted
    /// </summary>
    public class StrokeEventArgs : EventArgs
    {
        /// <summary>
        /// Stroke ID
        /// </summary>
        public int StrokeId;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="id">Stroke id</param>
        public StrokeEventArgs(int id)
        {
            StrokeId = id;
        }
    }

    #region Native enums

    /// <summary>
    /// Enum for pen types
    /// </summary>
    public enum IC_PENTYPE
    {
        NORMAL,
        HIGHLIGHTER
    };


    
    /// <summary>
    /// Enum for canvas types
    /// </summary>
    public enum IC_CANVASTYPE
    {
        FIXEDCANVAS,
        VARIABLECANVAS
    };


    
    /// <summary>
    /// Enum for editing mode
    /// </summary>
    public enum IC_EDITINGMODE
    {
        INK = 1,
        SELECT = 2,
        DELETE = 3
    };

    
    /// <summary>
    /// Enum for ink encoding types
    /// </summary>
    public enum IC_INKENCODING
    {
        BINARY,
        BASE64,
        GIF,
        BASE64GIF
    };


    /// <summary>
    /// The penstyle structure
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct PENSTYLE
    {
        /// <summary>
        /// The size of the structure
        /// </summary>
        public int cbSize;
        /// <summary>
        /// Widht of the pen in mm
        /// </summary>
        [MarshalAs(UnmanagedType.R4)]
        public float dPenWidth;           
        /// <summary>
        /// The color of the pen
        /// </summary>
        public int crColor;
        /// <summary>
        /// The type of the pen
        /// </summary>
        public IC_PENTYPE ptPenType;
    }


    /// <summary>
    /// The structure for notifying the WM_CHAR message
    /// NM_CHAR is not defined on Windows CE
    /// </summary>
    public struct NMCHR
    {
        public NMHDR hdr;              // contains additional info. about this notification
        public uint ch;              // Character that is being processed. The wParam of the WM_CHAR message
        public uint info;             // lParam of the WM_CHAR message
    }


    /// <summary>
    /// The structure for notifying the ICNM_STROKEADDED
    /// and ICNM_STROKEDELETED message
    /// </summary>
    public struct NMSTROKE
    {
        public NMHDR hdr;              // contains additional info. about this notification
        public int lStrokeID;        // Stroke ID of the added/deleted strokes. If multiple strokes 
        // are added/deleted, this is 0.
    }

    /// <summary>
    /// InkCanvas and InkControl Window Style definitions
    /// </summary>
    public enum ICS
    {
        /// <summary>
        /// This flag creates a fixed canvas 
        /// </summary>
        FIXEDCANVAS = 0x00010000,
    }





    /// <summary>
    /// User defined messages that InkCanvas will handle
    /// </summary>
    public enum ICM
    {
        /// <summary>
        /// Base
        /// </summary>
        BASE = (WM.USER + 500),

        /// <summary>
        /// --------------------------------------------------------------------------
        /// ICM_GETMODE
        //
        /// Returns the current editing mode
        //
        /// lParam = (LPARAM) 0
        /// wParam = (WPARAM) 0
        //
        /// Return value
        /// 
        ///      editingmode: One of the values of IC_EDITINGMODE
        //
        /// --------------------------------------------------------------------------
        /// </summary>
        GETMODE = BASE + 1,


        /// <summary>
        /// --------------------------------------------------------------------------
        /// ICM_SETMODE
        //
        /// Sets the editing mode
        //
        /// lParam = (LPARAM) 0
        /// wParam = (WPARAM) (IC_EDITINGMODE) editingmode
        //
        //
        /// Parameters
        //
        ///  editingmode
        /// 
        ///  The mode to which we wish to switch. One of IC_EDITINGMODE values.
        //
        /// Return value
        //
        ///  Returns 0 if there are errors
        /// --------------------------------------------------------------------------
        /// </summary>
        SETMODE = BASE + 2,


        /// <summary>
        /// --------------------------------------------------------------------------
        /// ICM_GETINK
        //
        /// Returns a reference to the ink object in the canvas
        //
        /// lParam = (LPARAM) (IInkDisp**) ppInk
        /// wparam = (WPARAM) 0
        //
        //
        /// Parameters
        //
        ///  ppInk
        //
        ///  Pointer to a pointer to an ink object. Should not be NULL.
        //
        /// Return value
        //
        ///  Returns 0 if there are errors
        /// --------------------------------------------------------------------------
        /// </summary>
        GETINK = BASE + 3,


        /// <summary>
        /// --------------------------------------------------------------------------
        /// ICM_GETINKDATA
        //
        /// Returns the Ink data in the requested format
        //
        /// lParam = (LPARAM) (VARIANT*) pvData
        /// wParam = (WPARAM) (IC_INKENCODING) encoding
        //
        //
        /// Parameters
        //
        ///  pvData
        ///  
        ///  Pointer to a variant. The caller has to call VariantClear (to free the 
        ///  memory allocated for the data). 
        //
        ///  encoding
        //
        ///  The encoding in which the data is required. One of the IC_INKENCODING values.
        //
        /// Return value
        /// 
        ///  Returns 0 if there are errors.
        /// 
        /// Remarks
        ///  Upon successfully saving the data *pvData will have the save ink data
        ///  Variant type is either 
        ///     VT_UI1|VT_ARRAY   - for binary formats: isf and gif
        ///     VT_BSTR           - for base64 formats: base64isf and base64gif
        ///  Caller has to call VariantClear() to free the memory allocated
        /// --------------------------------------------------------------------------
        /// </summary>
        GETINKDATA = BASE + 4,


        /// --------------------------------------------------------------------------
        /// ICM_GETINKDATAASBITMAP
        //
        /// Returns a bitmap of the ink on the canvas.
        //
        /// lParam = (LPARAM) (HBITMAP*) pHBitmap
        /// wParam = (WPARAM) 0
        //
        //
        /// Parameters
        /// 
        ///  pHBitmap
        /// 
        ///  Pointer to an HBITMAP. The bitmap is created by us and its the caller's 
        ///  responsibility to free it.
        //
        /// Return value
        /// 
        ///  Returns 0 if there are errors.
        /// --------------------------------------------------------------------------
        GETINKDATAASBITMAP = BASE + 5,


        /// <summary>
        /// --------------------------------------------------------------------------
        /// ICM_LOADINKDATA
        //
        /// Loads the ink data supplied into the canvas. This discards the existing 
        /// ink data.
        //
        /// lParam = (LPARAM) (VARIANT*) pvData
        /// wParam = (WPARAM) 0
        //
        //
        /// Parameters
        /// 
        ///  pvData
        /// 
        ///  Pointer to a variant. The data supplied in the variant is loaded into the ink
        ///  on the canvas, replacing whatever is already there.
        //
        /// Return value
        /// 
        ///  Returns 0 if there are errors.
        //
        /// Remarks
        ///  Variant type is either 
        ///     VT_UI1|VT_ARRAY   - for binary formats: isf and gif
        ///     VT_BSTR           - for base64 formats: base64isf and base64gif
        /// --------------------------------------------------------------------------
        /// </summary>
        LOADINKDATA = BASE + 6,


        /// <summary>
        /// --------------------------------------------------------------------------
        /// ICM_CLEARCANVAS
        //
        /// Clears the canvas. This action cannot be undone.
        //
        /// lParam = (LPARAM) 0
        /// wParam = (WPARAM) 0
        //
        //
        /// Return value
        /// 
        ///  Returns 0 if there are errors.
        /// --------------------------------------------------------------------------
        /// </summary>
        CLEARCANVAS = BASE + 7,


        // --------------------------------------------------------------------------
        // ICM_SETPENSTYLE
        //
        // Sets the supplied penstyle as the new penstyle for the canvas.
        //
        // lParam = (LPARAM) (PENSTYLE*) pPenstyle
        // wParam = (WPARAM) 0
        //
        // Parameters
        //
        //  pPenstyle 
        //
        //  Pointer to a penstyle structure which has the new penstyle info. The size field
        //  (cbSize) must be filled in by the caller.
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        SETPENSTYLE = BASE + 8,


        // --------------------------------------------------------------------------
        // ICM_GETPENSTYLE
        //
        // Returns the current penstyle being used.
        //
        // lParam = (LPARAM) (PENSTYLE*) pPenstyle
        // wParam = (WPARAM) 0
        //
        // Parameters
        //
        //  pPenstyle 
        //
        //  Pointer to a penstyle structure. This is where we fill in the current penstyle
        //  info. The structure should be allocated by the caller and the size field (cbSize) 
        //  must be filled in by the caller.
        //  
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        GETPENSTYLE = BASE + 9,


        // --------------------------------------------------------------------------
        // ICM_SETKBKGNDCOLOR
        //
        // Sets the background color of the canvas
        //
        // lParam = (LPARAM) (COLOREF) color
        // wParam = (WPARAM) 0
        //
        // Parameters
        //
        //  color
        //
        //  The color which we want to set the background to. To create a COLORREF value, 
        //  use the RGB macro.
        //  
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        SETBKGNDCOLOR = BASE + 10,


        // --------------------------------------------------------------------------
        // ICM_CANCOPY
        //
        // Returns if it's possible to copy data to the clipboard.
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  nSelected: The number of strokes that can be copied. 0 if no data can be copied.
        // --------------------------------------------------------------------------
        CANCOPY = BASE + 11,


        // --------------------------------------------------------------------------
        // ICM_CANPASTE
        //
        // Returns if it's possible to paste data from the clipboard to the canvas
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  Returns 0 if data cannot be pasted
        // --------------------------------------------------------------------------
        CANPASTE = BASE + 12,


        // --------------------------------------------------------------------------
        // ICM_CANUNDO
        //
        // Returns if an undo operation can be performed
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  Returns 1 if undo can be performed
        // --------------------------------------------------------------------------
        CANUNDO = BASE + 13,


        // --------------------------------------------------------------------------
        // ICM_CANREDO
        //
        // Returns if a redo operation can be performed
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  Returns 1 if a redo can be performed
        // --------------------------------------------------------------------------
        CANREDO = BASE + 14,


        // --------------------------------------------------------------------------
        // ICM_GETZOOM
        //
        // Returns the current zoom level
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  nZoomLevel: The current zoom level in percentage
        // --------------------------------------------------------------------------
        GETZOOM = BASE + 15,


        // --------------------------------------------------------------------------
        // ICM_SETZOOM
        //
        // Sets the current zoom level
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) (LONG) nZoomLevel
        //
        // Parameters
        //
        //  nZoomlevel
        //
        //  Zoom level as a percentage. The range is from 20% to 500%
        //
        // 
        // Return value
        // 
        //  Returns 0 if there are errors
        // --------------------------------------------------------------------------
        SETZOOM = BASE + 16,


        // --------------------------------------------------------------------------
        // ICM_GETCANVASSIZE
        //
        // Returns the size of the canvas in pixels.
        //
        // lParam = (LPARAM) (SIZE*) pCanvasSize
        // wParam = (WPARAM) 0
        //
        // Parameters
        //
        //  pCanvasSize
        //
        //  Pointer to a user allocated SIZE structure. The dimensions returned are in 
        //  pixels and they are the dimensions of the actual canvas.
        //  
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        GETCANVASSIZE = BASE + 18,


        // --------------------------------------------------------------------------
        // ICM_SETCANVASSIZE
        //
        // Set the size of the current canvas
        //
        // lParam = (LPARAM) (SIZE*) pCanvasSize
        // wParam = (WPARAM) 0
        //
        // Parameters
        //
        //  pCanvasSize
        //
        //  Pointer to a SIZE structure. The dimensions are in pixles. The ink object is 
        //  clipped to fit inside the canvas size supplied.
        //  
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        SETCANVASSIZE = BASE + 19,


        // --------------------------------------------------------------------------
        // ICM_REDO
        //
        // Redo the last undone action.
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  Returns 0 if redo was unsuccessful
        // --------------------------------------------------------------------------
        REDO = BASE + 20,


        // --------------------------------------------------------------------------
        // ICM_UNDO
        //
        // Undo the most recent undoable action
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  Returns 0 if undo was unsuccessful
        // --------------------------------------------------------------------------
        UNDO = BASE + 40,


        // --------------------------------------------------------------------------
        // ICM_SETCANVASCOLOR
        //
        // Sets the color of the canvas.
        //
        // lParam = (LPARAM) (COLORREF) color
        // wParam = (WPARAM) 0
        //
        // Parameters
        //
        //  color
        //
        //  The color which we want to set the canvas to. To create a COLORREF value, 
        //  use the RGB macro. Note that this call is different from ICM_SETBKGNDCOLOR.
        //  That color is used to paint the region on the window which lies outside the
        //  canvas.
        //  
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        SETCANVASCOLOR = BASE + 21,


        // --------------------------------------------------------------------------
        // ICM_ERASEALLINK
        //
        // Erase all the ink on the canvas. This action can be undone.
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        ERASEALLINK = BASE + 22,


        // --------------------------------------------------------------------------
        // ICM_SETAUTOSCROLL
        //
        // Use this to enable/disable autoscrolling when the user inks near the end of
        // the canvas window. By default autoscroll is off. This is only applicable for
        // scrolling vertically.
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) (VARIANT_BOOL) vbAutoScroll
        //
        // Parameters
        //
        //  vbAutoScroll
        //
        //  VARIANT_BOOL indicating whether scrolling should be on(VARIANT_TRUE) or 
        //  off(VARIANT_FALSE).
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        SETAUTOSCROLL = BASE + 23,


        // --------------------------------------------------------------------------
        // ICM_SETAUTOEXPAND
        //
        // Turns canvas expansion on or off. When enabled, the canvas is expanded when the 
        // user inks near the edge of the canvas. Canvas expansion happens in both directions. 
        // 
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) (VARIANT_BOOL) vbAutoScroll
        //
        // Parameters
        //
        //  vbAutoScroll
        //
        //  VARIANT_BOOL indicating whether expansion should be on(VARIANT_TRUE) or 
        //  off(VARIANT_FALSE).
        //
        // Note
        //  This can be enabled only for variable canvas types. This message is different 
        //  from autoscroll. This merely expands the canvas but doesn't neccessarily scroll it. 
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        SETAUTOEXPAND = BASE + 24,


        // --------------------------------------------------------------------------
        // ICM_GETUNDOCOUNT
        //
        // Returns the number of undo operations that can be performed.
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        //
        // Return value
        // 
        //  nUndoCount: The number of undo operations that can be performed.
        // --------------------------------------------------------------------------
        GETUNDOCOUNT = BASE + 25,


        // --------------------------------------------------------------------------
        // ICM_GETNOTIFICATIONMASK
        //
        // Returns the mask of the notifications the user has subscribed to.
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) 0
        //
        // Note
        //  By default the ICMASK_DEFAULT is used. 
        //  
        // Return value
        // 
        //  lMask: The current notification mask.
        // --------------------------------------------------------------------------
        GETNOTIFICATIONMASK = BASE + 26,


        // --------------------------------------------------------------------------
        // ICM_SETNOTIFICATIONMASK
        //
        // Sets the mask of the notifications one would like to receive.
        // 
        //
        // lParam = (LPARAM) 0
        // wParam = (WPARAM) (LONG) lMask
        //
        // Parameters
        //
        //  lMask
        //
        //  This could be a bitwise OR of the various notifications. ICNM_xxx defines
        //  the notifications that canvas provides.
        //
        // Return value
        // 
        //  Returns 0 if there are errors.
        // --------------------------------------------------------------------------
        SETNOTIFICATIONMASK = BASE + 27,
    }


    /////////////////////////////////////////////////////////////////////////////
    // Notification codes that canvas passes on to the parent window by sending the
    // WM_NOTIFY message. 
    /////////////////////////////////////////////////////////////////////////////


    public enum ICMN
    {
        // Base for the notification codes
        BASE = ICM.BASE + 100,


        // --------------------------------------------------------------------------
        // ICNM_CHAR 
        //
        // A WM_NOTIFY message is sent to the parent with this notification code when
        // we don't handle the WM_CHAR message. 
        //
        // lParam = (LPARAM) (LPNMHDR) pNmChar
        // wParam = (WPARAM) (int) idCtrl
        //
        // Parameters
        //
        //  pNmChar 
        //
        //  Pointer to an NMCHR structure containing information about the character.
        //
        //  idCtrl
        // 
        //  The control ID of this window
        //
        // --------------------------------------------------------------------------
        CHAR = BASE + 1,


        // --------------------------------------------------------------------------
        // ICNM_STROKEADDED
        //
        // A WM_NOTIFY message is sent to the parent with this notification code when
        // a new stroke is added.
        //
        // lParam = (LPARAM) (NMSTROKE*) pNmStroke
        // wParam = (WPARAM) (int) idCtrl
        //
        // Parameters
        //
        //  pNmStroke
        //
        //  Pointer to an NMSTROKE structure containing information about the stroke added.
        //
        //  idCtrl
        // 
        //  The control ID of this window
        //
        // --------------------------------------------------------------------------
        STROKEADDED = BASE + 2,


        // --------------------------------------------------------------------------
        // ICNM_STROKEDELETED
        //
        // A WM_NOTIFY message is sent to the parent with this notification code when
        // stroke(s) are deleted.
        //
        // lParam = (LPARAM) (NMSTROKE*) pNmStroke
        // wParam = (WPARAM) (int) idCtrl
        //
        // Parameters
        //
        //  pNmStroke
        //
        //  Pointer to an NMSTROKE structure containing information about the deleted stroke(s).
        //
        //  idCtrl
        // 
        //  The control ID of this window
        //
        // --------------------------------------------------------------------------
        STROKEDELETED = BASE + 4,
    }

    /// <summary>
    /// Notification masks that apps can set for receiving notifications
    /// </summary>
    [Flags]
    public enum ICMASK
    {
        /// <summary>
        /// Mask set to receive the ICNM_CHAR notification
        /// </summary>
        CHAR = 0x00000001,

        /// <summary>
        /// Mask set to receive the ICNM_STROKEADDED notification
        /// </summary>
        STROKEADDED = 0x00000002,

        /// <summary>
        /// Mask set to receive the ICNM_STROKEDELETED notification
        /// </summary>
        STROKEDELETED = 0x00000004,

        /// <summary>
        /// The default mask. Only forwards the ICNM_CHAR notification
        /// </summary>
        DEFAULT = 0x00000001,

    }

    #endregion
}
